# -*- coding: utf-8 -*-
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
from sklearn.cross_decomposition import PLSRegression  #偏最小二乘法的实现，  在这里是可以跳进 PLSRegression 里面的
import numpy as np
import os
import pickle

import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
#定义全局函数的部分。
def error(y_predict,y_test):                            #定义计算误差平方和函数，，，传入的是估算出的值，和测试值，，这里仅仅是用来定义的，方便后面的调用。
    sum=0.0
    for i in range(len(y_predict)):
        e = (y_predict[i][0]-y_test[i][0])**2
        # errs.append(e)
        sum+=e
    return sum

def pls2(x_train, y_train, degree):
    n_components = 0
    score = []
    #
    while n_components < x_train.shape[1]:
        n_components = n_components + 1  # 在第一遍的时候n_components是1，第二遍循环的时候是2，，，第n遍循环是n，，最大是x的列数，也就是特征的个数，
        # pls2 = PLSRegression(n_components=n_components)  # 计算SS （SS这个是全建模 ， PRESS是减去一个进行建模，，，，在python里建模很简单，设置好参数，调用一下函数就能建模了）
        # # 这个不是偏最小二乘法吗？？？,,这里是循环计算主成分的个数，直到达到满意的精度。
        # pls2.fit(x_train_st, y_train)  # fit也是一个函数，，，两个参数，第一个参数是训练集，第二个参数是目标。
        # y_predict0 = pls2.predict(x_test_st)  # predict也是一个内置的函数，，，，这个是不是用建好的模型去做预测，，，，把参数训练集输入进去，得到的是预测的值。
        # SS = error(y_predict0, y_test)  # 这里是预测的值和真正的值之间的误差大小。
        # # print(SS)

        y_predict1 = []  # 这是创建了一个新的变量。根据名字的意思，根据模型得到的y的预测值，实际上这个模型是留一法建立的模型。
        for i in range(x_train.shape[0]):  # 计算PRESS，，，，这个是x_train_st的行数
            n_components1 = n_components  # 但是我不明白，为什么这里还要加1，主成分不可能是0个吧，所以就从1开始了。
            x_train_st1 = np.delete(x_train, i, 0)  # 这里的0是行，1是列，，这个应该是删除第i行，，，这里是标准化的数组。留一法的实现
            y_train_st1 = np.delete(y_train, i, 0)  # 这个也是删除第i行，这里都是经过标准化的（但这个x是经过标准化的，y却没有用标准化的数据）。，，这个没有用到过，是不是这里写错了？？
            pls2 = PLSRegression(n_components=n_components1)  # 偏最小二乘法参数的设置，，，这里面一般有5个参数，，但这里只传入了主成分的个数。
            # 参数1：n_components:int ,(default 2) ,,要保留的主成分数量，默认为2
            # 参数2：scale:boolean,(default True),,是否归一化数据，默认为是
            # 参数3：max_iter: an integer,(default 500),,使用NIPALS时的最大迭代次数
            # 参数4：tol： non-negative real（default 1e-06）,,迭代截止条件
            # 参数5：copy： Boolean，（default True）
            pls2.fit(x_train_st1, y_train_st1)  # 这里是根据前面设置好的参数建模过程，这里的建模过程是不是不太对（这里x是归一化的，y并没有用归一化的），应该都是用归一化的才行呀。？？？

            # 这里主要是进行了数据格式的转换，因为做预测要传进的是矩阵【格式很重要】
            x_train_st_i = []  # 用之前要进行清空，这个很重要。用一个参数之前要进行清空。
            x_train_st_list = x_train[i].tolist()
            x_train_st_i.append(x_train_st_list)
            # print('the x_train_st is ', x_train_st_i)  # 输出一下变量，查看格式是否正确，因为下面的predict函数需要用[[1，1，1，1，1，1]] 这种格式的数据

            y_predict11 = pls2.predict(x_train_st_i)  # 预测函数，给定之前留一法没有用到的样本，进行建模，预测对应的y值。？？？？可是已经删除了不是吗？？？           ZHE这句出了一点问题？？？？就是数据格式有问题，需要在最外面在加一个[]
            # print(y_predict11)
            y_predict1.append(y_predict11)  # 把所有的y值组成一个数组，便于计算误差。，这个也是y的预测值，用它来算出另一个误差。
        PRESS = error(y_predict1, y_train)  # 可能错误：https://blog.csdn.net/little_bobo/article/details/78861578
        score.append(PRESS)

    n_components = score.index(min(score))

    return n_components+1

